import * as Sequelize from 'sequelize'
import * as _ from 'lodash'
import Q from 'q'
import { get_table_define } from './Functions';
import {IServers} from '../interface/IFace'
import { db } from './Db';
export class Model{
    protected static _db:any=null;
    protected _model:any=null;
    protected _true_table_name:string="";
    protected _fields:any=[];
    protected _table_name="";
    protected _transaction:Sequelize.Transaction;
    protected _options:any={
        fields:[],where:{}
    };
    protected _config:IServers;
    public static parseWhere(where){
        var w={};
        _.forOwn(where,(v,k)=>{
            if(_.isObject(v)){
                w[k]={};
                _.forOwn(v,(o,p)=>{
                    p = p.toLowerCase();
                    if(!_.isUndefined(Sequelize.Op[p])){
                        w[k][Sequelize.Op[p]]=o;
                    }else{
                        w[k][p]=o;
                    }
                })
            }else{
                w[k]=v;
            }
        })
        return w;
    }
    get config(){
        return this._config;
    }
    set config(config){
        if(_.isNull(config)){
            this._config = null;
        }else if(_.isObject(config)){
            this._config = Object.assign(_.isObject(this._config)?this._config:{},config)
        }else{

        }
    }
    get model(){
        return this.getModel()
    }
    public $search={
        // "tableName":['fields']
    }

    /**
     * 构造器
     * @param Table
     * @param {string} Prefix
     */
    constructor(Table:string,Config:IServers){
        this._config = Config;
        this._table_name=Table;
        if(Table.length>0)
            this._true_table_name=Config.database.prefix+Table.replace(/([A-Z])/g,function ($0,$1) {
                return '_'+$1.toLowerCase();
            }).replace(/_(.+)/,"$1")
        // this._db = db;
    }

    /**
     * 解析查询条件
     * @returns {{} | any}
     * @protected
     */
    protected _parse_where(where?:any) {
        return this._options.where;
    }
    /**
     * 获取查询字段
     * @returns {Array}
     * @protected
     */
    protected _parse_fields() {
        if(this._options.fields.length==0){
            return get_table_define(this._table_name,this._config)
        }
        return this._options.fields;
    }
    protected _parse_order(){
        var order=[];
        if('string' == typeof this._options.order){
            this._options.order = this._options.order.split(',')
        }
        if(_.isArray(this._options.order)){
            _.forOwn(this._options.order,(v,k)=>{
                order.push(v.split(' '))
            })
        }
        return order;
    }
    /**
     * 解析配置文件，生成查询属性
     * @returns {{}}
     * @protected
     */
    protected _parse_config() {
        let config = {};
        config['attributes']=this._parse_fields();
        config['where']=this._parse_where();
        if(this._options.order){
            config['order']=this._parse_order()
        }
        if(this._options.limit){
            config['limit']=this._options.limit
        }
        if(this._options.offset){
            config['offset']=this._options.offset
        }
        return config;
    }
    /**
     * 设置表的字段，默认读取所有的
     * @param fields
     */
    public setTableFields(fields) {
        this._fields=fields;
    }
    public define=function(config={}){
        //TODO 加载数据库表结构定义
        try{
            let table = require(this._config.root+`/Db/${this._table_name}.js`)
            if(table&&table[this._table_name]){
                config = table[this._table_name];
            }
        }catch(e){
            console.log(e);
        }
        return config;
    }
    /**
     * 获取一个Sequelize的模型
     * @returns {any}
     */
    public getModel() {
        if(!this._model){
            this._model = this.db.models[this._true_table_name]?this.db.models[this._true_table_name]:this.db.define(this._true_table_name, this.define(), {
                freezeTableName: true,
                timestamps: false
            })
        }
        return this._model;
    }
    public get db(){
        return db(this._config);
    }
    /**
     * 检测是否存在并在不存在的情况下添加数据
     * @param data
     */
    public async addIfNotExist(data,where=null){
        return this.where(where||data).find().then(d=>{
            if(_.isObject(d)){
                //存在
                return true;
            }else{
                //不存在
                return this.add(data)
            }
        })
    }
    public async findOrAdd(data:Object){
        return this.model.findOrCreate({
            where:this._parse_where(),
            defaults:data
        })
    }
    /**
     * 设定Where条件
     * @param where
     * @returns {Model.where}
     */
    public where = function (where){
        if(null == where){
            this._options.where={};
        }else
        if(_.isObject(where))
            this._options.where=Object.assign(this._options.where,where);
        else{
            console.log('Error Where',where)
        }
        return this;
    }
    /**
     * 设定字段列表，支持数组和字符串格式
     * @param {Number | String} fields
     * @param {boolean} exclude
     * @returns {Model.fields}
     */
    public fields(fields:string|Array<string>,exclude=false) {
        if(_.isArray(fields)){
            this._options.fields = _.concat(this._options.fields,fields)
        }else if(_.isString(fields)){
            this._options.fields = _.concat(this._options.fields,fields.split(','))
        }
        return this;
    }
    /**
     * 设定排序规则，
     * @param {String} order
     * @returns {Model.order}
     */
    public order(order:String) {
        this._options.order=order;
        return this;
    }
    /**
     * 发起查询请求
     * @returns {Bluebird<any[]>}
     */
    public async select() {
        return  this.getModel().findAll(this._parse_config(),this.executeOption).then(d=>{
            let data = [];
            this._clean();
            d.forEach(v=>{
                data.push(v.dataValues)
            })
            return data;
        });
    }
    public async add(data) {
        return  this.getModel().create(data,this.executeOption).then(d=>{
            this._clean();
            return d.dataValues
        })
    }
    public data(data) {
        this.getModel().build(data)
        return this;
    }
    /**
     * 查找一个
     */
    public async find() {
        return  this.limit(1).select().then(d=>{
            this._clean();
            return d[0]
        });
    }
    /**
     * 批量添加数据
     * @param data
     * @returns {any}
     */
    public async addAll(data) {
        return this.getModel().bulkCreate(data,{
            fields:Object.keys(data[0])
        },this.executeOption).then(d=>{
            this._clean();
            let data = [];
            d.forEach(v=>{
                data.push(v.dataValues)
            })
            return data;
        })
    }
    /**
     * 取数量
     */
    public count() {

    }
    /**
     * 支持selectAndCount
     * @returns {Promise<{count; rows: any[]}>}
     */
    public async selectAndCount(){
        return  this.getModel().findAndCountAll(this._parse_config(),this.executeOption).then(d=>{
            let data = [];
            d.rows.forEach(v=>{
                data.push(v.dataValues)
            })
            this._clean();
            return {
                count:d.count,
                rows:data
            };
        });
    }
    /**
     * 设置limit参数，
     * @param {number} Number
     */
    public limit(Number:number) {
        this._options.limit=Number;
        return this;
    }
    /**
     * 设置分页参数
     * @param {number} Page
     * @param {number} Number
     * @returns {Model.page}
     */
    public page(p:number,n:number) {
        this._options.limit=Number(n);
        this._options.offset=(p-1)*n;
        return this;
    }
    /**
     * 调用delete语句
     * @returns {any}
     */
    public async del() {
        return this.getModel().destroy({
            where:this._parse_where(this._options.where)
        },this.executeOption).then(d=>{
            this._clean();
            return d;
        })
    }
    /**
     * 调用save方法
     * @param data
     */
    public async save(data) {
        return  this.getModel().update(data,{
            where:this._parse_where(this._options.where),
            options:{
                returning:true
            }
        },this.executeOption).then(d=>{
            this._clean();
            return d;
        })
    }
    /**
     * 执行自定义请求
     * @param sql
     * @returns {any}
     */
    public async query(sql, option:Object={}) {
        return  this.db.query(sql.replace(/__DB_PREFIX__/g,this._config.database.prefix),Object.assign(option,this.executeOption));
    }
    /**
     * 开启事物
     * @returns {Sequelize.Transaction | any}
     */
    public async startTrans() {
        let transaction =  this.getModel().transaction()
        return transaction;
    }
    protected get executeOption(){        
        let option:any={}
        if(!_.isUndefined(this._transaction)){
            option.transaction=this._transaction
        }
        return option;
    }
    /**
     * 提交
     */
    public async commit() {
         this._transaction.commit();
        this._transaction = undefined;
    }
    /**
     * 回滚
     */
    public async rollback() {
         this._transaction.rollback();
        this._transaction = undefined;
    }
    /**
     * 清除查询条件，
     * @protected
     */
    protected _clean() {
        this._options.fields=[];
        this._options.where={};
        if(!_.isUndefined(this._options.limit))
            delete this._options.limit;
        if(!_.isUndefined(this._options.offset))
            delete this._options.offset;
    }
    /**
     * 开启事务查询
     * @param Transaction 
     */
    public transaction(Transaction:Sequelize.Transaction){
        this._transaction = Transaction;
        return this;
    }
    /**
     * 获取某个字段
     * @param Fields
     * @param {boolean} More
     * @returns {any}
     */
    public async getFields(Fields,More=false){
        if(!More){
            this.page(1,1)
        }
        if(_.isString(Fields)){
            Fields=Fields.split(',')
        }
        if(Fields.length>0)
            return  this.fields(Fields).select().then(d=>{
                this._clean();
                var pk = Fields[0];
                if(d.length>0){
                    if(More){
                        var data = {};
                        var odata=[];
                        _.forOwn(d,(v,k)=>{
                            if(Fields.length==1){
                                odata.push(v[pk])
                            }else{
                                data[v[pk]]=v;
                            }
                        })
                        return Fields.length==1?odata:data;
                    }else{
                        if(Fields.length==1){
                            return d[0][pk];
                        }else{
                            return d[0];
                        }
                    }
                }
                return "";
            })
        else{
            return Q({})
        }
    }
}